/*
 *    Copyright © OpenAtom Foundation.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package com.inspur.edp.udt.core.Manager;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.inspur.edp.cef.api.dataType.valueObj.ICefValueObject;
import com.inspur.edp.cef.api.determination.NestedTransmitType;
import com.inspur.edp.cef.api.manager.serialize.CefSerializeContext;
import com.inspur.edp.cef.api.manager.valueObj.ICefValueObjManagerContext;
import com.inspur.edp.cef.api.resourceInfo.ValueObjModelResourceInfos;
import com.inspur.edp.cef.core.manager.CefValueObjManager;
import com.inspur.edp.cef.entity.changeset.IChangeDetail;
import com.inspur.edp.cef.entity.changeset.ValueObjModifyChangeDetail;
import com.inspur.edp.cef.entity.entity.ICefData;
import com.inspur.edp.cef.entity.entity.IValueObjData;
import com.inspur.edp.cef.spi.entity.info.propertyinfo.DataTypePropertyInfo;
import com.inspur.edp.cef.spi.entity.resourceInfo.ValueObjModelResInfo;
import com.inspur.edp.cef.spi.jsonser.abstractcefchange.AbstractCefDataDeSerializer;
import com.inspur.edp.cef.spi.jsonser.abstractcefchange.AbstractCefDataSerializer;
import com.inspur.edp.udt.api.Manager.IUdtManager;
import com.inspur.edp.udt.api.Udt.IUdtContext;
import com.inspur.edp.udt.core.Udt.AbstractUdt;
import com.inspur.edp.udt.core.Udt.UdtContext;
import com.inspur.edp.udt.core.Udt.UdtMgrResInfoImpl;
import com.inspur.edp.udt.core.Udt.UdtResInfoImpl;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

public abstract class AbstractUdtManager extends CefValueObjManager  implements IUdtManager {

    private Map<Boolean, ObjectMapper> cache = new java.util.HashMap<Boolean, ObjectMapper>();
    private Object locker = new Object();

    @Override
    protected ICefValueObjManagerContext createMgrContext() {
        return new UdtMgrContext();
    }

    protected abstract JsonSerializer<ICefData> getDataSerializer();

    protected abstract JsonDeserializer<ICefData> getDataDeserializer();



    @Override
    public String serialize(ICefData data) {
        try {
            return getEntityJsonMapper().writeValueAsString(data);
        } catch (JsonProcessingException e) {
            throw new RuntimeException("序列化失败", e);
        }
    }

    @Override
    public ICefData deserialize(String content) {
        try {
            return getEntityJsonMapper().readValue(content, ICefData.class);
        } catch (IOException e) {
            throw new RuntimeException("反序列化失败", e);
        }
    }

    private ObjectMapper getEntityJsonMapper() {

        CefSerializeContext context = getSerizlizeContext();
        boolean enableStdTimeFormat = false;
        if (context != null)
            enableStdTimeFormat = context.getEnableStdTimeFormat();
        if (cache.containsKey(enableStdTimeFormat))
            return cache.get(enableStdTimeFormat);

        synchronized (locker) {
            if (!cache.containsKey(enableStdTimeFormat)) {
                ObjectMapper mapper = new ObjectMapper();
                SimpleModule module = new SimpleModule();
                JsonSerializer serializer = getDataSerializer();
                if (serializer instanceof AbstractCefDataSerializer) {
                    AbstractCefDataSerializer cefSerializer = (AbstractCefDataSerializer) serializer;
                    cefSerializer.setCefSerializeContext(context);
                }
                module.addSerializer(ICefData.class, serializer);

                JsonDeserializer deserializer = getDataDeserializer();
                if (deserializer instanceof AbstractCefDataDeSerializer) {
                    AbstractCefDataDeSerializer cefdeserializer = (AbstractCefDataDeSerializer) deserializer;
                    cefdeserializer.setCefSerializeContext(context);
                }
                module.addDeserializer(ICefData.class, deserializer);
                mapper.registerModule(module);

                cache.put(enableStdTimeFormat, mapper);
            }
            return cache.get(enableStdTimeFormat);
        }

    }

    @Override
    public final ICefValueObject createValueObject(IValueObjData data) {
        UdtContext tempVar = new UdtContext();
        tempVar.setData(data);
        tempVar.setModelResInfo(getModelInfo());
        UdtContext udtCtx = tempVar;
        AbstractUdt rez = createAbstractUdt(udtCtx);
        return rez;
    }

    public abstract AbstractUdt createAbstractUdt(IUdtContext ctx);

//	/**
//	 * 国际化资源-模型
//	 *
//	 * @return
//	 */
//	public com.inspur.edp.cef.api.resourceInfo.ValueObjModelResInfo modelResourceInfos() {
//		return null;
//	}

    public ValueObjModelResInfo getModelInfo() {
        return getUdtModelInfo();
    }


    protected UdtMgrResInfoImpl getUdtModelInfo() {
        return null;
    }

    @Override
    public NestedTransmitType getTransmitType() {
        ValueObjModelResInfo info = getModelInfo();
        if (info == null) {
            return super.getTransmitType();
        }
        return info.getTransmitType();
    }

    @Override
    public ICefData deserialize(HashMap<String, JsonNode> nodes, ICefData data) {
        if (data == null)
            data = createDataType();

        UdtResInfoImpl resInfo = (UdtResInfoImpl)getModelInfo().getCustomResource("");
        for(Map.Entry<String, JsonNode> nodePair : nodes.entrySet()){
            String key = nodePair.getKey();
            DataTypePropertyInfo propInfo = resInfo.getEntityTypeInfo().getPropertyInfo(key);
            if(propInfo == null){
                propInfo = resInfo.getEntityTypeInfo().getPropertyInfo(resInfo.getEntityCode());
            }

            Object value = propInfo.read(nodePair.getValue(), getSerizlizeContext());
            data.setValue(propInfo.getPropertyName(), value);
        }

        return data;
    }

    @Override
    public IChangeDetail deserializeChange(HashMap<String, JsonNode> nodes, IChangeDetail changeDetail) {
        if (changeDetail == null)
            changeDetail = new ValueObjModifyChangeDetail();
        UdtResInfoImpl resInfo = (UdtResInfoImpl)getModelInfo().getCustomResource("");
        for(Map.Entry<String, JsonNode> nodePair : nodes.entrySet()){
            String key = nodePair.getKey();
            DataTypePropertyInfo propInfo = resInfo.getEntityTypeInfo().getPropertyInfo(key);
            if(propInfo == null){
                propInfo = resInfo.getEntityTypeInfo().getPropertyInfo(resInfo.getEntityCode());
            }

            Object value = propInfo.readChange(nodePair.getValue(), getSerizlizeContext());
            ((ValueObjModifyChangeDetail)changeDetail).setItem(propInfo.getPropertyName(), value);
        }
        return changeDetail;
    }
}
